﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using IO = System.IO;
using System.IO;

namespace MassIntellect.WorkIt.Frames
{
    public class DirectoryObserverLoader : WorkFrame
    {
        /// <summary>
        /// Каталог, из которго будут загружаться файлы
        /// </summary>
        [Serialize(IgnoreDefault = true)]
        public string Directory { get; set; }

        /// <summary>
        /// Каталог, в который будут помещаться загруженные файлы
        /// </summary>
        [Serialize(IgnoreDefault = true)]
        public string SubdirBackUp { get; set; }

        /// <summary>
        /// Шаблон поиска файлов
        /// </summary>
        [Serialize(IgnoreDefault = true)]
        public string SearchPattern { get; set; }

        /// <summary>
        /// Ключ-аргумент для загруженного потока
        /// </summary>
        [Serialize(IgnoreDefault = true)]
        public string ContentKey { get; set; }

        /// <summary>
        /// Ключ аргумента "Имя файла"
        /// </summary>
        [Serialize(IgnoreDefault = true)]
        public string FileNameKey { get; set; }

        /// <summary>
        /// Сохранение прочтённого файла
        /// </summary>
        [Serialize(IgnoreDefault = true)]
        public bool IsBackuping { get; set; }

        /// <summary>
        /// Чтение файла полностью в память (рекомендуется, т.к. слежение за закрытием потока перекладывается на следующие кадры), иначе создается файловый поток
        /// </summary>
        [Serialize]
        public bool IsReadToMemory { get; set; }

        /// <summary>
        /// Деактивировать кадр, если каталог чтения пуст и отсутствуют открытые потоки
        /// </summary>
        [Serialize]
        public bool IsDeactivateIfEmpty { get; set; }

        /// <summary>
        /// Не деактивировать кадр пока не будет загружено, заданное или большее кол-во файлов
        /// </summary>
        [Serialize]
        public int WaitCounterFile { get; set; }

        /// <summary>
        /// Вырезать расширение файла
        /// </summary>
        [Serialize]
        public bool IsExludeExtensionInName { get; set; }

        private void CreateDirectoriesIfNeeded(out string mainDir, out string backUpSubDir)
        {
            mainDir = CombineDirectory(Directory);
            if (!IO.Directory.Exists(mainDir))
                IO.Directory.CreateDirectory(mainDir);
            if (IsBackuping)
            {
                backUpSubDir = CombineDirectory(Path.Combine(mainDir, SubdirBackUp));
                if (!IO.Directory.Exists(backUpSubDir))
                    IO.Directory.CreateDirectory(backUpSubDir);
            }
            else
                backUpSubDir = "";
        }

        private static string CombineDirectory(string path)
        {
            var sb = new StringBuilder(Path.Combine(IO.Directory.GetCurrentDirectory(), path));
            if (sb.Length > 0 && sb[sb.Length - 1] != Path.DirectorySeparatorChar)
                sb.Append(Path.DirectorySeparatorChar);
            return sb.ToString();
        }

        private void CheckOpenedStreams(string backupdir)
        {
            foreach (var item in openedStreams.ToArray())
                if (!item.Value.CanSeek)
                {
                    item.Value.Dispose();
                    //Thread.Sleep(100);
                    MoveFile(backupdir, item.Key);
                    openedStreams.Remove(item.Key);
                }
        }

        private void AddOpenedStreams(string path, FileStream fs)
        {
            openedStreams.Add(path, fs);
            Log("Создан поток файла '{0}'", path);
            orderLoad.Remove(path);
        }

        private void MoveFile(string backupdir, string path)
        {
            WaitCounterFile--;             
            if (IsBackuping)                            
                File.Move(path, PathExtension.GetAlternativeFilePath(Path.Combine(backupdir, Path.GetFileName(path))));            
            else
                File.Delete(path);
            orderLoad.Remove(path);
        }

        private void UpdateOrder(string dir)
        {
            foreach (var item in IO.Directory.GetFiles(dir, GetKeyOrDefault(SearchPattern, "*.*"))
                .Where(s => !orderLoad.Contains(s) && !openedStreams.ContainsKey(s)))
                orderLoad.Add(item);
        }

        private readonly Dictionary<string, FileStream> openedStreams = new Dictionary<string, FileStream>();

        private readonly HashSet<string> orderLoad = new HashSet<string>(StringComparer.CurrentCultureIgnoreCase);

        protected override void protectedPulse(PulseArgs args)
        {
            string dir, backupdir;
            CreateDirectoriesIfNeeded(out dir, out backupdir);
            CheckOpenedStreams(backupdir);
            UpdateOrder(dir);
            if (orderLoad.Count > 0)
            {
                var item = orderLoad.First();
                var a2 = args.Clone();
                var fn = Path.GetFileName(item);
                a2[GetKeyOrDefault(FileNameKey, "FileName")]
                    = IsExludeExtensionInName ? Path.GetFileNameWithoutExtension(item) : fn;
                if (IsReadToMemory)
                {
                    var buff = new byte[2048];
                    int readed;
                    var ms = new MemoryStream();
                    using (var fs = new FileStream(item, FileMode.Open, FileAccess.Read))
                        while ((readed = fs.Read(buff, 0, buff.Length)) > 0)
                            ms.Write(buff, 0, readed);
                    //Thread.Sleep(100);
                    Log("Загружен файл '{0}' в память.", item);
                    MoveFile(backupdir, item);
                    a2[GetKeyOrDefault(ContentKey, "Content")] = ms;
                }
                else
                {
                    var fs = new FileStream(item, FileMode.Open, FileAccess.Read, FileShare.None);
                    
                    AddOpenedStreams(item, fs);
                    a2[GetKeyOrDefault(ContentKey, "Content")] = fs;
                }
                base.protectedPulse(a2);
            }
            if (orderLoad.Count == 0 && openedStreams.Count == 0 && IsDeactivateIfEmpty && WaitCounterFile <= 0)
                IsActive = false;
        }
    }
}
